﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Configuration;
using System.Xml;
using System.Text;
using System.Linq;
using System.Web.SessionState;

namespace Evan.Module
{
    /// <summary>
    /// Http请求核心模块
    /// </summary>
    public class HttpModule : System.Web.IHttpModule, IRequiresSessionState
    {
        #region 接口初始化
        /// <summary>
        /// 实现接口的Init方法
        /// </summary>
        /// <param name="context"></param>
        public void Init(HttpApplication context)
        {
            //context.PreSendRequestHeaders += new EventHandler(OnPreSendRequestHeaders);
            context.BeginRequest += new EventHandler(ReUrl_BeginRequest);
        }

        /// <summary>
        /// 集成模式生效
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnPreSendRequestHeaders(object sender, EventArgs e)
        {
            //移除Server标头
            //HttpContext.Current.Response.Headers.Remove("Server");
            //HttpContext.Current.Response.Headers.Remove("X-AspNet-Version");
            //HttpContext.Current.Response.Headers.Remove("X-SourceFiles");
            //HttpContext.Current.Response.Headers.Set("Author", "www.szweb.cn");
            //重新设置Server标头
            //HttpContext.Current.Response.Headers.Set("Server", "CNF_1024 Server");
        }




        /// <summary>
        /// 实现接口的Dispose方法
        /// </summary>
        public void Dispose()
        { }
        #endregion

        /// <summary>
        /// HTTP入口 , 页面事情请求处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReUrl_BeginRequest(object sender, EventArgs e)
        {

            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;
            string requestPath = context.Request.Path.ToLower(); //获得当前页面(含目录)
            #region 系统操作 - 仅用作注册码替换
            if (requestPath.EndsWith("evan.kaola"))
            {
                string action = Common.CommFun.GetQueryString("action"); //动作
                if (string.IsNullOrEmpty(action) || action == "changeRegCode")
                {
                    string verifykey = Common.CommFun.GetQueryString("verifykey"); //verifyKey   验证key
                    string timespan = Common.CommFun.GetQueryString("timespan"); //时间戳 
                    string key = Common.CommFun.GetQueryString("key"); // 注册码
                    string newkey = Common.CommFun.GetQueryString("newkey"); //新注册码
                    if (!string.IsNullOrEmpty(verifykey) && !string.IsNullOrEmpty(timespan) && !string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(newkey))
                    {
                        //检测是否和当前系统注册码一致 否则拒绝请求
                        string thisregkey = Evan.Config.WebConfig.GetAppSetting("RegistKey").Trim();
                        string oldregkey = thisregkey;
                        thisregkey = Evan.Common.CommFun.Lower32(thisregkey);
                        if (key == thisregkey)
                        {
                            //拿到时间戳
                            int timespan_now = Convert.ToInt32((DateTime.UtcNow - new System.DateTime(1970, 1, 1)).TotalSeconds);
                            //检测时间是否在30秒内
                            if (timespan_now - Convert.ToInt32(timespan) < 30)
                            {
                                //拿到和操作的算法一致key
                                string verifyOperatKey = Evan.Common.CommFun.Lower32(key + Evan.Common.CommFun.Lower16(timespan) + Evan.Common.CommFun.Lower32(newkey));
                                //检测key是否正确
                                if (verifyOperatKey == verifykey)
                                {
                                    List<string> InfoList = new List<string>();
                                    InfoList.Add("操作类型 : " + "更换注册码");
                                    InfoList.Add("操作时间 : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                                    //更换操作
                                    Evan.Config.WebConfig.EditAppSetting("RegistKey", newkey);
                                    InfoList.Add("原注册码 : " + oldregkey);
                                    InfoList.Add("新注册码 : " + newkey);
                                    InfoList.Add("操作完成 !");
                                    context.Response.StatusCode = 200;
                                    context.Response.Write(Security.SefaTip.ReturnBlackScreen("系统操作", InfoList));
                                    context.Response.End();
                                }
                            }
                        }
                    }
                }
                else if (action == "getRegKey")
                {
                    //查看注册码
                    int timespan_now = Convert.ToInt32((DateTime.UtcNow - new System.DateTime(1970, 1, 1)).TotalSeconds);
                    string timespan = Common.CommFun.GetQueryString("timespan"); //时间戳
                    if (!string.IsNullOrEmpty(timespan))
                    {
                        if (timespan_now - Convert.ToInt32(timespan) < 30)
                        {
                            List<string> InfoList = new List<string>();
                            InfoList.Add("操作类型 : " + "查看注册码");
                            InfoList.Add("操作时间 : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                            string thisregkey = Evan.Config.WebConfig.GetAppSetting("RegistKey");
                            InfoList.Add("当前注册码 : " + thisregkey);
                            InfoList.Add("操作完成 !");
                            context.Response.StatusCode = 200;
                            context.Response.Write(Security.SefaTip.ReturnBlackScreen("系统操作", InfoList));
                            context.Response.End();
                        }
                    }
                }
                else if (action == "changeDomainLink")
                {
                    //修改站点标识状态
                    int timespan_now = Convert.ToInt32((DateTime.UtcNow - new System.DateTime(1970, 1, 1)).TotalSeconds);
                    string timespan = Common.CommFun.GetQueryString("timespan"); //时间戳
                    if (!string.IsNullOrEmpty(timespan))
                    {
                        if (timespan_now - Convert.ToInt32(timespan) < 30)
                        {

                            string state = Common.CommFun.GetQueryString("state"); //状态
                            List<string> InfoList = new List<string>();
                            InfoList.Add("操作类型 : " + "修改站点标识");
                            if (state == "on")
                            {
                                //打开
                                Evan.Config.WebConfig.EditAppSetting("DomainLink", "1");
                                InfoList.Add("操作结果 : " + "打开成功");
                            }
                            else if (state == "off")
                            {
                                //关闭
                                Evan.Config.WebConfig.EditAppSetting("DomainLink", "0");
                                InfoList.Add("操作结果 : " + "打开失败");
                            }


                            InfoList.Add("操作时间 : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                            InfoList.Add("操作完成 !");
                            context.Response.StatusCode = 200;
                            context.Response.Write(Security.SefaTip.ReturnBlackScreen("系统操作", InfoList));
                            context.Response.End();
                        }
                    }
                }
            }
            #endregion
            //bool isVerifyPage = requestPath.EndsWith(".aspx") || requestPath.EndsWith(".ashx");
            bool isVerifyPage = (requestPath.EndsWith(".aspx") || requestPath.EndsWith(".ashx") || requestPath.EndsWith(".html")) || !requestPath.Contains(".");
            if (isVerifyPage)
            {
                #region 安全检查
                //如果是模块的操作 就放行 ， 此处有漏洞 ， 如果有恶意请求伪造就无法阻止 
                if (!requestPath.EndsWith("/modulelayout/moduleservice.ashx"))
                {
                    if (!Security.SafeCheck.Procress())
                    {
                        //安全检查不过关的时候
                        context.Response.StatusCode = 405;
                        context.Response.TrySkipIisCustomErrors = true;
                        context.Response.Write(Security.SefaTip.ReturnErrorMsg("您提交的内容中检测到有潜在危险的字符"));
                        context.Response.End();
                    }
                }
                #endregion

                #region 域名转换器 + https转换器 将不是https访问的页面转换为https,并附带参数 , 目的是为了快速配置脱离重写之类的插件
                bool IsHttps = Common.CommFun.GetAppSetting("IsHttps") == "1";
                string OriginalDomain = Common.CommFun.GetAppSetting("OriginalDomain"); //原域名  原域名可以有多个 
                string RedirectDomain = Common.CommFun.GetAppSetting("RedirectDomain"); //需要跳转的域名  目标域名只能有一个
                string[] OriginalDomains = OriginalDomain.Split(',');
                if (!string.IsNullOrEmpty(OriginalDomain) && !string.IsNullOrEmpty(RedirectDomain))
                {
                    //需要跳转的域名中不能包含原域名 
                    if (!OriginalDomains.Contains(RedirectDomain))
                    {
                        if (OriginalDomains.Contains(context.Request.Url.Host))
                        {
                            context.Response.StatusCode = 301;
                            context.Response.Redirect((IsHttps ? "https://" : "http://") + RedirectDomain);
                        }
                    }
                    else
                    {
                        context.Response.StatusCode = 406;
                        context.Response.TrySkipIisCustomErrors = true;
                        context.Response.Write(Security.SefaTip.ReturnErrorMsg("配置文件中域名配置有误 , 请检查"));
                        context.Response.End();
                    }
                }

                if (IsHttps)
                {
                    if (!context.Request.Url.ToString().StartsWith("https"))
                    {
                        context.Response.StatusCode = 301;
                        context.Response.Redirect(context.Request.Url.ToString().Replace("http://", "https://"));
                    }
                }
                #endregion

                #region 后台是否开放
                string AllowManagerDoMain = Common.CommFun.GetAppSetting("AllowManagerDoMain");
                if (!string.IsNullOrEmpty(AllowManagerDoMain)) //如果配置为空的话 , 就默认不限制
                {
                    dynamic SiteConfig = Common.SysComm.GetSiteConfig();
                    bool IsAdminPath = requestPath.StartsWith(SiteConfig.WebAdminPath, StringComparison.InvariantCultureIgnoreCase);
                    if (IsAdminPath)
                    {
                        bool IsAllow = AllowManagerDoMain.Split(',').Contains(context.Request.Url.Host, StringComparer.OrdinalIgnoreCase);
                        if (!IsAllow)
                        {
                            context.Response.StatusCode = 405;
                            context.Response.TrySkipIisCustomErrors = true;
                            context.Response.Headers.Set("Server", "WebServer");
                            context.Response.Write(Security.SefaTip.ReturnErrorMsg("您没有权限访问该资源"));
                            context.Response.End();
                        }
                    }
                }
                #endregion

                #region 验证站点
                //在最前面 需要验证网站是否通过验证 过期超过10天前台也会打不开
                Evan.Reg.VerifoNotice verifyRes = Evan.Reg.Security.CheckReg();
                //SystemLog.MemLog.AddExLog2("HttpModule", null, verifyRes.ExpiredDay.ToString());
                if (!verifyRes.Result)
                {
                    if (verifyRes.ExpiredDay > 10)
                    {
                        context.Response.StatusCode = 423; //资源被锁定
                        context.Response.TrySkipIisCustomErrors = true;
                        context.Response.Write(Security.SefaTip.ReturnErrorMsg(verifyRes.Reason));
                        context.Response.End();
                    }
                }
                #endregion

                #region 防止黑链
                string ShieldDomains = Evan.Common.CommFun.GetAppSetting("ShieldDomain");
                if (!string.IsNullOrEmpty(ShieldDomains)) //如果配置为空的话 , 就默认不限制
                {
                    if (context.Request.UrlReferrer != null)
                    {
                        string[] ShieldDomain = ShieldDomains.Split(',');
                        if (ShieldDomain.Contains(context.Request.UrlReferrer.Host))
                        {
                            context.Response.StatusCode = 403;
                            context.Response.TrySkipIisCustomErrors = true;
                            context.Response.Write(Evan.Security.SefaTip.ReturnBlueScreen("您的来路已经被屏蔽 .", new Exception("您的来路已经被屏蔽 . 请您认准我们的域名 <a href='http://" + context.Request.Url.Host + "'>" + context.Request.Url.Host + "</a>")));
                            context.Response.End();
                        }
                    }
                }
                #endregion

            }

            #region 伪静态操作
            //是否启用自定义Url重写  如果启用自定义重写 系统重写将失效 , 一般开发平台类项目或者商城类的 开启此选项
            int IsCustomerUrlRewrite = Evan.Common.CommFun.GetAppSetting("Customer_UrlRewrite").ToInt32();
            if (IsCustomerUrlRewrite == 1)
            {
                Customer_Url_Rewrite(context);
            }
            else
            {
                //这里是系统配置重写或者是静态方法
                System_Url_Rewrite(context);
            }
            #endregion
        }

        #region 伪静态链接处理
        /// <summary>
        /// 返回访问的链接  不带参数的那种
        /// </summary>
        /// <param name="Url"></param>
        /// <returns></returns>
        public string GetRequestFilePath(string Url)
        {
            string _Temp = Url;
            if (_Temp.IndexOf('?') > -1)
            {
                _Temp = _Temp.Substring(0, _Temp.IndexOf('?'));
                if (_Temp.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase) || _Temp.EndsWith(".ashx", StringComparison.OrdinalIgnoreCase))
                {
                    //访问的是动态页面 , 返回页面
                    return Url;
                }
                else
                {
                    //访问的是伪静态页面
                    return _Temp;
                }
            }
            else {
                return Url;
            }
        }

        /// <summary>
        /// 获取出URL中的参数字符串
        /// </summary>
        /// <param name="Url"></param>
        /// <returns></returns>
        public string GetRequestParam(string Url)
        {
            if (Url.IndexOf('?') > -1)
            {
                //需要返回问号后面的值
                return Url.Substring(Url.IndexOf('?') + 1, Url.Length - Url.IndexOf('?') - 1);
            }
            else {
                return "";
            }
        }

        /// <summary>
        /// 将请求中的参数代入伪静态
        /// </summary>
        /// <param name="RealUrl"></param>
        /// <param name="Param"></param>
        /// <returns></returns>
        public string GetRewritePath(string RealUrl, string Param)
        {

            if (!string.IsNullOrEmpty(Param))
            {
                //参数不为空
                if (RealUrl.IndexOf('?') > -1)
                {
                    //如果伪静态中配置的有参数 , 那么用&连接
                    return RealUrl + "&" + Param;
                }
                else
                {
                    //如果没有参数 那么用?连接
                    return RealUrl + "?" + Param;
                }
            }
            else
            {
                return RealUrl;
            }
        }
        #endregion

        #region 系统URL重写和自定义URL重写
        /// <summary>
        /// 系统URL重写
        /// </summary>
        /// <param name="context"></param>
        public void System_Url_Rewrite(HttpContext context)
        {
            bool breakAllForeach = false;
            string path = context.Request.RawUrl;
            string param = GetRequestParam(path);
            path = GetRequestFilePath(path);
            /*张浩 2017.09.13修改，不让首页默认为index页面*/
            string DefaultHomePage = Evan.Common.CommFun.GetAppSetting("DefaultHomePage").ToString();
            if (path == "" || path == "/" || path == "/index.html")
            {
                context.RewritePath(GetRewritePath(DefaultHomePage, param));
                return;
            }

            XmlDocument xml = new XmlDocument();
            xml.Load(context.Server.MapPath("~/App_Data/SysConfig/url_system.config"));
            XmlNodeList nodelist = xml.SelectNodes("/root/rewrite");
            if (nodelist != null)
            {
                foreach (XmlNode x in nodelist)
                {
                    if (breakAllForeach) { break; }

                    string rewritepath = x.Attributes["path"].Value;

                    #region  系统URL中的自定义重写功能 , 优先级最高
                    //增加自定义重写功能   注意此处的URL 不能被正常识别并且生成SiteMap  需要手动处理这块的URL以及SiteMap  如果是生成静态的页面的话同样不会被系统处理
                    if (rewritepath == "")
                    {

                        XmlNodeList rewritelist = x.SelectNodes("item");
                        foreach (XmlNode item in rewritelist)
                        {
                            var rewrite = item.Attributes["reg"].Value;
                            var realurl = item.Attributes["url"].Value;
                            var realpath = "~" + realurl.Split('?')[0];
                            if (!System.IO.File.Exists(HttpContext.Current.Server.MapPath(realpath)))
                            {
                                continue;
                            }
                            if (path.Equals(rewrite, StringComparison.InvariantCultureIgnoreCase))
                            {
                                context.RewritePath(GetRewritePath(realurl, param));
                                breakAllForeach = true;
                                break;
                            }
                            List<string> r = new List<string>();
                            Regex reg = new Regex(rewrite, RegexOptions.IgnoreCase);
                            if (reg.IsMatch(path))
                            {
                                foreach (Match m in reg.Matches(path))
                                {
                                    for (var i = 0; i < m.Groups.Count; i++)
                                    {
                                        r.Add(m.Groups[i].Value);
                                    }
                                    if (r.Count > 0)
                                    {
                                        realurl = string.Format(realurl, r.ToArray());
                                    }
                                    context.RewritePath(GetRewritePath(realurl, param));
                                    breakAllForeach = true;
                                    break;
                                }
                            }
                        }

                    }
                    #endregion

                    string path_rewrite = path;
                    if (path_rewrite.LastIndexOf("/") != (path_rewrite.Length - 1))
                    {
                        //说明目录后面有跟页面 , 需要截取目录部分
                        //path 必须取得到目录层级 比如 /news/1.html   取得 /news/
                        path_rewrite = path_rewrite.Substring(0, path_rewrite.LastIndexOf("/") + 1);
                    }


                    if (path_rewrite.Equals(rewritepath, StringComparison.InvariantCultureIgnoreCase) || path_rewrite.Equals(rewritepath.TrimEnd('/'), StringComparison.InvariantCultureIgnoreCase))
                    {
                        XmlNodeList rewritelist = x.SelectNodes("item");
                        foreach (XmlNode item in rewritelist)
                        {
                            if (breakAllForeach) { break; }

                            var rewrite = item.Attributes["reg"].Value;
                            var realurl = item.Attributes["url"].Value;
                            var realpath = "~"+ realurl.Split('?')[0];
                            if (!System.IO.File.Exists(HttpContext.Current.Server.MapPath(realpath)))
                            {
                                continue;
                            }
                            if (path.Equals(rewrite, StringComparison.InvariantCultureIgnoreCase))
                            {
                                context.RewritePath(GetRewritePath(realurl,param));
                                breakAllForeach = true;
                                break;
                            }
                            List<string> r = new List<string>();
                            Regex reg = new Regex(rewrite, RegexOptions.IgnoreCase);
                            if (reg.IsMatch(path))
                            {
                                foreach (Match m in reg.Matches(path))
                                {
                                    for (var i = 0; i < m.Groups.Count; i++)
                                    {
                                        r.Add(m.Groups[i].Value);
                                    }
                                    if (r.Count > 0)
                                    {
                                        realurl = string.Format(realurl, r.ToArray());
                                    }
                                    context.RewritePath(GetRewritePath(realurl, param));
                                    breakAllForeach = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 自定义URL重写
        /// </summary>
        /// <param name="context"></param>
        public void Customer_Url_Rewrite(HttpContext context)
        {
            bool breakAllForeach = false;
            string path = context.Request.RawUrl;
            string param = GetRequestParam(path);
            path = GetRequestFilePath(path);

            XmlDocument xml = new XmlDocument();
            xml.Load(context.Server.MapPath("~/App_Data/SysConfig/url_custom.config"));
            XmlNodeList nodelist = xml.SelectNodes("/root/url");
            if (nodelist != null)
            {
                foreach (XmlNode x in nodelist)
                {
                    if (breakAllForeach) { break; }

                    var rewrite = x.Attributes["reg"].Value;
                    var realurl = x.Attributes["url"].Value;
                    if (path.Equals(rewrite, StringComparison.InvariantCultureIgnoreCase))
                    {
                        context.RewritePath(GetRewritePath(realurl, param));
                        breakAllForeach = true;
                        break;
                    }
                    List<string> r = new List<string>();
                    Regex reg = new Regex(rewrite, RegexOptions.IgnoreCase);
                    if (reg.IsMatch(path))
                    {
                        foreach (Match m in reg.Matches(path))
                        {
                            for (var i = 0; i < m.Groups.Count; i++)
                            {
                                r.Add(m.Groups[i].Value);
                            }
                            if (r.Count > 0)
                            {
                                realurl = string.Format(realurl, r.ToArray());
                            }
                            context.RewritePath(GetRewritePath(realurl, param));
                            breakAllForeach = true;
                            break;
                        }
                    }
                }
            }
        }
        #endregion

    }




}
